package org.god.core;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.god.utils.Resources;

import javax.sql.DataSource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/*sqlSessionFactory构造器*/
public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(InputStream inputStream) throws DocumentException {
        SqlSessionFactory sqlSessionFactory =null;
        try {
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();
            Element environmentsElt = (Element) document.selectSingleNode("/configuration/environments");
            String defaultEnv = environmentsElt.attributeValue("default");
            Element environmentElt = (Element) document.selectSingleNode("/configuration/environments/environment[@id='" + defaultEnv + "']");
            Element dataSourceElt = (Element) environmentElt.selectSingleNode("dataSource");
            Element transactionManagerElt = (Element) environmentElt.selectSingleNode("transactionManager");
            DataSource dataSource=getDataSource(dataSourceElt);
            Transaction transaction=getTransaction(transactionManagerElt,dataSource);
            Element mappersElt  = rootElement.element("mappers");
            List<String> mapperPathList=new ArrayList<>();
            List<Element> mappers = mappersElt.elements();
            mapperPathList=mappers.stream().map(mapper -> mapper.attributeValue("resource")).collect(Collectors.toList());

            Map<String, MappedStatement> mappedStatement = getMappedStatement(mapperPathList);
            sqlSessionFactory=new SqlSessionFactory(transaction,mappedStatement);

        }catch (Exception e){
            e.printStackTrace();
        }
        return sqlSessionFactory;

    }

    private Map<String, MappedStatement> getMappedStatement(List<String> mapperPathList) {
        Map<String, MappedStatement> mappedStatementList = new HashMap<>();
        mapperPathList.forEach(mapperPath -> {
            SAXReader saxReader = new SAXReader();
            try {
                InputStream inputStream = Resources.getResourceAsStream(mapperPath);
                Document document = saxReader.read(inputStream);
                Element mapper= (Element) document.selectSingleNode("/mapper");
                String namespace = mapper.attributeValue("namespace");
                List<Element> elements = mapper.elements();
                elements.forEach(element -> {
                    String id = element.attributeValue("id");
                    String sqlId=namespace+"."+id;
                    String resultType = element.attributeValue("resultType");
                    String sql=element.getTextTrim();
                    MappedStatement mappedStatement = new MappedStatement(sql, resultType);
                    mappedStatementList.put(sqlId, mappedStatement);
                });
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        });
        return mappedStatementList;
    }

    private DataSource getDataSource(Element dataSourceElt) {
        Map<String, String> properties = new HashMap<>();
        List <Element> propertiesElt = dataSourceElt.elements();
        propertiesElt.forEach(element -> {
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            properties.put(name, value);
        });
        String type = dataSourceElt.attributeValue("type");
        DataSource dataSource=null;
        switch (type){
            case Const.UNPOOLED -> {
                dataSource = new UnpooledDataSource(
                        properties.get("driver"),
                        properties.get("url"),
                        properties.get("username"),
                        properties.get("password")
                );
            }
            case Const.POOL -> {

            }
            case Const.JDNIDATASOURCE -> {

            }
        }
        return dataSource;
    }

    private Transaction getTransaction(Element transactionManagerElt, DataSource dataSource) {
        String type = transactionManagerElt.attributeValue("type");
        Transaction transaction=null;
        switch (type){
            case Const.JDBCTRANCATION -> {
                transaction= new JdbcTransaction(dataSource, false);
            }
            case Const.MANAGERTRANCATION -> {
                transaction= new ManagerTrancation();
            }
        }
        return transaction;
    }


}
